package com.socket;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

/**
 * 聊天室服务端
 */
public class Server {
    /*
        java.net.ServerSocket "总机"
        ServerSocket类似于服务端的"总机",该设备上有若干的"插座"
        每个插座可以连接一个客户端的插座，实现让多个客户端连接到同一个"总机"上

        ServerSocket有两个操作:
        1:实例化时向系统申请服务端口，客户端先通过这个端口与总机建立连接
        2:一旦一个客户端与总机建立连接，总机就会分配一个"插座"与该客户端沟通
     */
    private ServerSocket serverSocket;

    public Server() {
        try {
            System.out.println("正在启动服务端...");
            /*
                实例化ServerSocket是要指定服务端口，客户端就是通过这个端口
                找到服务端程序的。
                该端口不能与当前计算机上其他应用程序申请的端口重复，否则会抛出
                异常:java.net.BindException: Address already in use:
             */
            serverSocket = new ServerSocket(8088);
            System.out.println("服务端启动完毕");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start() {
        try {
            while (true) {
                System.out.println("等待客户端连接...");
                /*
                ServerSocket上一个重要的方法:
                Socket accept()
                该方法用于接受客户端的连接，此方法是一个阻塞方法，调用后方法会"卡住"
                开始等待客户端的连接，一旦一个客户端连接，那么此方法会立即返回一个
                Socket实例，通过这个Socket就可以与刚建立连接的客户端网络交互了
             */
                Socket socket = serverSocket.accept();
                System.out.println("一个客户端连接了!");
                //启动一个线程负责处理该客户端交互
                ClientHandler handler = new ClientHandler(socket);
                Thread t = new Thread(handler);
                t.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Server server = new Server();
        server.start();
    }

    /**
     * 该线程任务是负责与指定的客户端进行交互
     */
    private class ClientHandler implements Runnable {
        //对应客户端的Socket
        private Socket socket;

        //对应客户端的ip地址
        private String host;

        //对应客户端的昵称
        private String nickName;

        public ClientHandler(Socket socket) {
            this.socket = socket;
            this.host = socket.getInetAddress().getHostAddress();
        }

        public void run() {
            try {
                /*
                    Socket提供的方法:
                    InputStream getInputStream()
                    通过该Socket获取的输入流可以读取到来自对应客户端发送过来的字节数据
                 */
                InputStream in = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);
                BufferedReader br = new BufferedReader(isr);
                nickName = br.readLine();
                String message;
                /*
                    服务端调用br.readLine()读取客户端发送过来的一行字符串时可能出现异常:
                    java.net.SocketException: Connection reset
                    原因:客户端异常断开(没有进行四次挥手)
                 */
                while ((message = br.readLine()) != null) {
                    System.out.println(nickName + "[" + host + "]说:" + message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}
